//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <core.h>
#include <_hal.h>
#include <_schedule.h>

//
// DzEvent
//
// IMPORTANT: when disable interrupt
void DzEvent::BindEventSet(EventSetRefNode *pEventSetRefNode)
{
    assert(pEventSetRefNode);
    m_eventSetRefList.InsertLast(pEventSetRefNode);
}

// IMPORTANT: when disable interrupt
void DzEvent::UnbindEventSet(EventSetRefNode *pEventSetRefNode)
{
    assert(pEventSetRefNode);
    pEventSetRefNode->Detach();
}

// IMPORTANT: when disable interrupt
INLINE void DzEvent::WaiterReadyToUninterruptibleSleep()
{
    register Thread *pCurrentThread = GetCurrentThread();

//    WillToInheritPriority(pCurrentThread);

    ReadyToUninterruptibleSleepOn(this);

    InsertWaiter(pCurrentThread);
}

// IMPORTANT: when disable interrupt
INLINE void DzEvent::WaiterReadyToUninterruptibleSleep(DzTimer *pBindingTimer)
{
    assert(pBindingTimer);

    register Thread *pCurrentThread = GetCurrentThread();

//    WillToInheritPriority(pCurrentThread);

    ReadyToUninterruptibleSleepOn(this, pBindingTimer);

    pBindingTimer->_Start();
    InsertWaiter(pCurrentThread);
}

// IMPORTANT: when disable interrupt
INLINE void DzEvent::WaiterReadyToSleep(WaitResult *pResult)
{
    assert(pResult);

    register Thread *pCurrentThread = GetCurrentThread();

//    WillToInheritPriority(pCurrentThread);

    ReadyToSleepOn(this, NULL, pResult);
    if (WaitResult_OK != *pResult) {
        return;
    }

    InsertWaiter(pCurrentThread);

    *pResult = WaitResult_OK;
}

// IMPORTANT: when disable interrupt
INLINE void DzEvent::WaiterReadyToSleep(
    DzTimer *pBindingTimer, WaitResult *pResult)
{
    assert(pBindingTimer);
    assert(pResult);

    register Thread *pCurrentThread = GetCurrentThread();

//    WillToInheritPriority(pCurrentThread);

    ReadyToSleepOn(this, pBindingTimer, pResult);
    if (WaitResult_OK != *pResult) {
        return;
    }

    pBindingTimer->_Start();
    InsertWaiter(pCurrentThread);

    *pResult = WaitResult_OK;
}

void DzEvent::UninterruptibleWait(EventState *pState)
{
    uint_t uState;
    uint32_t uFlags;

    if (m_bManualReset) {
        uFlags = SaveFlagsAndCli();
        if (UNSIGNALED == (uState = m_uState)) {
            WaiterReadyToUninterruptibleSleep();
            RestoreIF(uFlags);

            Schedule();

            if (NULL != pState) *pState = GetCurrentThread()->GetEventState();
        }
        else {
            RestoreIF(uFlags);

            if (NULL != pState) *pState = uState;
        }
    }
    else {
        uFlags = SaveFlagsAndCli();
        if (UNSIGNALED == (uState = InterlockedExchange(
                (Int32 *)&m_uState, UNSIGNALED))) {
            WaiterReadyToUninterruptibleSleep();
            RestoreIF(uFlags);

            Schedule();

            if (NULL != pState) *pState = GetCurrentThread()->GetEventState();
        }
        else {
            RestoreIF(uFlags);

            if (NULL != pState) *pState = uState;
        }
    }
}

void DzEvent::Wait(WaitResult *pResult, EventState *pState)
{
    assert(pResult);

    uint_t uState;
    uint32_t uFlags;

    if (m_bManualReset) {
        uFlags = SaveFlagsAndCli();
        if (UNSIGNALED == (uState = m_uState)) {
            WaiterReadyToSleep(pResult);
            if (WaitResult_OK != *pResult) {
                RestoreIF(uFlags);
                return;
            }
            RestoreIF(uFlags);

            Schedule();

            if (ClearInterrupted()) {
                *pResult = WaitResult_Interrupted;
                return;
            }

            if (NULL != pState) *pState = GetCurrentThread()->GetEventState();
        }
        else {
            RestoreIF(uFlags);

            if (NULL != pState) *pState = uState;
        }
    }
    else {
        uFlags = SaveFlagsAndCli();
        if (UNSIGNALED == (uState = InterlockedExchange(
                (Int32 *)&m_uState, UNSIGNALED))) {
            WaiterReadyToSleep(pResult);
            if (WaitResult_OK != *pResult) {
                RestoreIF(uFlags);
                return;
            }
            RestoreIF(uFlags);

            Schedule();

            if (ClearInterrupted()) {
                *pResult = WaitResult_Interrupted;
                return;
            }

            if (NULL != pState) *pState = GetCurrentThread()->GetEventState();
        }
        else {
            RestoreIF(uFlags);

            if (NULL != pState) *pState = uState;
        }
    }

    *pResult = WaitResult_OK;
}

void DzEvent::TryUninterruptibleWait(
    uint_t ticks, WaitResult *pResult, EventState *pState)
{
    assert(pResult);

    uint_t uState;
    uint32_t uFlags;

    DzTimer timer(ScheduleTimeout, GetCurrentThread());

    if (m_bManualReset) {
        uFlags = SaveFlagsAndCli();
        if (UNSIGNALED == (uState = m_uState)) {
            if (0 == ticks) {
                RestoreIF(uFlags);
                *pResult = WaitResult_TimedOut;
                return;
            }
            timer.Set(ticks);
            WaiterReadyToUninterruptibleSleep(&timer);
            RestoreIF(uFlags);

            Schedule();

            if (timer.Due()) {
                *pResult = WaitResult_TimedOut;
                return;
            }

            if (NULL != pState) *pState = GetCurrentThread()->GetEventState();
        }
        else {
            RestoreIF(uFlags);

            if (NULL != pState) *pState = uState;
        }
    }
    else {
        uFlags = SaveFlagsAndCli();
        if (UNSIGNALED == (uState = InterlockedExchange(
                (Int32 *)&m_uState, UNSIGNALED))) {
            if (0 == ticks) {
                RestoreIF(uFlags);
                *pResult = WaitResult_TimedOut;
                return;
            }
            timer.Set(ticks);
            WaiterReadyToUninterruptibleSleep(&timer);
            RestoreIF(uFlags);

            Schedule();

            if (timer.Due()) {
                *pResult = WaitResult_TimedOut;
                return;
            }

            if (NULL != pState) *pState = GetCurrentThread()->GetEventState();
        }
        else {
            RestoreIF(uFlags);

            if (NULL != pState) *pState = uState;
        }
    }

    *pResult = WaitResult_OK;
}

void DzEvent::TryWait(uint_t ticks, WaitResult *pResult, EventState *pState)
{
    assert(pResult);

    uint_t uState;
    uint32_t uFlags;

    DzTimer timer(ScheduleTimeout, GetCurrentThread());

    if (m_bManualReset) {
        uFlags = SaveFlagsAndCli();
        if (UNSIGNALED == (uState = m_uState)) {
            if (0 == ticks) {
                RestoreIF(uFlags);
                *pResult = WaitResult_TimedOut;
                return;
            }
            timer.Set(ticks);
            WaiterReadyToSleep(&timer, pResult);
            if (WaitResult_OK != *pResult) {
                RestoreIF(uFlags);
                return;
            }
            RestoreIF(uFlags);

            Schedule();

            if (ClearInterrupted()) {
                *pResult = WaitResult_Interrupted;
                return;
            }

            if (timer.Due()) {
                *pResult = WaitResult_TimedOut;
                return;
            }

            if (NULL != pState) *pState = GetCurrentThread()->GetEventState();
        }
        else {
            RestoreIF(uFlags);

            if (NULL != pState) *pState = uState;
        }
    }
    else {
        uFlags = SaveFlagsAndCli();
        if (UNSIGNALED == (uState = InterlockedExchange(
                (Int32 *)&m_uState, UNSIGNALED))) {
            if (0 == ticks) {
                RestoreIF(uFlags);
                *pResult = WaitResult_TimedOut;
                return;
            }
            timer.Set(ticks);
            WaiterReadyToSleep(&timer, pResult);
            if (WaitResult_OK != *pResult) {
                RestoreIF(uFlags);
                return;
            }
            RestoreIF(uFlags);

            Schedule();

            if (ClearInterrupted()) {
                *pResult = WaitResult_Interrupted;
                return;
            }

            if (timer.Due()) {
                *pResult = WaitResult_TimedOut;
                return;
            }

            if (NULL != pState) *pState = GetCurrentThread()->GetEventState();
        }
        else {
            RestoreIF(uFlags);
            if (NULL != pState) *pState = uState;
        }
    }

    *pResult = WaitResult_OK;
}

void DzEvent::Notify(EventState state)
{
    assert(UNSIGNALED != state);

    if (m_bManualReset)
        WakeUpAllWaiters(state);
    else
        WakeUpOneWaiter(state);
}

void DzEvent::NotifyByIsr(EventState state)
{
    assert(UNSIGNALED != state);

    if (m_bManualReset)
        WakeUpAllWaitersByIsr(state);
    else
        WakeUpOneWaiterByIsr(state);
}

void DzEvent::WakeUpOneWaiter(uint_t uState)
{
    register Thread *pWaiter;

    m_waitListLock.Lock();
    uint32_t uFlags = SaveFlagsAndCli();
    if (!m_waitListHead.IsEmpty()) {
        pWaiter = (Thread *)m_waitListHead.First();
        pWaiter->CancelBindingTimer();
        pWaiter->DetachFromSyncObject(this);
        RestoreIF(uFlags);

        pWaiter->SetEventState(uState);
        pWaiter->ReadyToWakeUp();
        m_waitListLock.Unlock();

        Schedule();

        return;
    }

    EventSetRefNode *pEventSetRefNode;
    ForEachDLinkNode(EventSetRefNode *, pEventSetRefNode, &m_eventSetRefList) {
        register DzEventSet *pEventSet = pEventSetRefNode->pEventSet;
        if (!pEventSet->m_waitListHead.IsEmpty()) {
            pWaiter = (Thread *)pEventSet->m_waitListHead.First();
            pWaiter->CancelBindingTimer();
            pWaiter->DetachFromSyncObject(pEventSet);
            RestoreIF(uFlags);

            pWaiter->SetEventState(pEventSetRefNode->uIndex, uState);
            pWaiter->ReadyToWakeUp();
            m_waitListLock.Unlock();

            Schedule();

            return;
        }
    }

    m_uState = uState;
    RestoreIF(uFlags);
    m_waitListLock.Unlock();
}

void DzEvent::WakeUpAllWaiters(uint_t uState)
{
    register Thread *pWaiter;

    m_waitListLock.Lock();
    m_uState = uState;

    uint32_t uFlags = SaveFlagsAndCli();
    while (!m_waitListHead.IsEmpty()) {
        pWaiter = (Thread *)m_waitListHead.First();
        pWaiter->CancelBindingTimer();
        pWaiter->DetachFromSyncObject(this);
        RestoreIF(uFlags);

        pWaiter->SetEventState(uState);
        pWaiter->ReadyToWakeUp();

        uFlags = SaveFlagsAndCli();
    }

    EventSetRefNode *pEventSetRefNode;
    ForEachDLinkNode(EventSetRefNode *, pEventSetRefNode, &m_eventSetRefList) {
        register DzEventSet *pEventSet = pEventSetRefNode->pEventSet;
        while (!pEventSet->m_waitListHead.IsEmpty()) {
            pWaiter = (Thread *)pEventSet->m_waitListHead.First();
            pWaiter->CancelBindingTimer();
            pWaiter->DetachFromSyncObject(pEventSet);
            RestoreIF(uFlags);

            pWaiter->SetEventState(pEventSetRefNode->uIndex, uState);
            pWaiter->ReadyToWakeUp();

            uFlags = SaveFlagsAndCli();
        }
    }

    RestoreIF(uFlags);
    m_waitListLock.Unlock();

    Schedule();
}

void DzEvent::WakeUpOneWaiterByIsr(uint_t uState)
{
    register Thread *pWaiter;

    uint32_t uFlags = SaveFlagsAndCli();
    if (!m_waitListHead.IsEmpty()) {
        pWaiter = (Thread *)m_waitListHead.First();
        pWaiter->CancelBindingTimer();
        pWaiter->DetachFromSyncObject(this);
        RestoreIF(uFlags);

        pWaiter->SetEventState(uState);
        pWaiter->ReadyToWakeUpByIsr();

#ifndef _neptune
        NeedReschedule();
#endif

        return;
    }

    EventSetRefNode *pEventSetRefNode;
    ForEachDLinkNode(EventSetRefNode *, pEventSetRefNode, &m_eventSetRefList) {
        register DzEventSet *pEventSet = pEventSetRefNode->pEventSet;
        if (!pEventSet->m_waitListHead.IsEmpty()) {
            pWaiter = (Thread *)pEventSet->m_waitListHead.First();
            pWaiter->CancelBindingTimer();
            pWaiter->DetachFromSyncObject(pEventSet);
            RestoreIF(uFlags);

            pWaiter->SetEventState(pEventSetRefNode->uIndex, uState);
            pWaiter->ReadyToWakeUpByIsr();

#ifndef _neptune
            NeedReschedule();
#endif

            return;
        }
    }

    m_uState = uState;
    RestoreIF(uFlags);
}

void DzEvent::WakeUpAllWaitersByIsr(uint_t uState)
{
    register Thread *pWaiter;

    m_uState = uState;

    uint32_t uFlags = SaveFlagsAndCli();
    while (!m_waitListHead.IsEmpty()) {
        pWaiter = (Thread *)m_waitListHead.First();
        pWaiter->CancelBindingTimer();
        pWaiter->DetachFromSyncObject(this);
        pWaiter->SetEventState(uState);
        pWaiter->ReadyToWakeUpByIsr();
    }

    EventSetRefNode *pEventSetRefNode;
    ForEachDLinkNode(EventSetRefNode *, pEventSetRefNode, &m_eventSetRefList) {
        register DzEventSet *pEventSet = pEventSetRefNode->pEventSet;
        while (!pEventSet->m_waitListHead.IsEmpty()) {
            pWaiter = (Thread *)pEventSet->m_waitListHead.First();
            pWaiter->CancelBindingTimer();
            pWaiter->DetachFromSyncObject(pEventSet);
            pWaiter->SetEventState(pEventSetRefNode->uIndex, uState);
            pWaiter->ReadyToWakeUpByIsr();
        }
    }
    RestoreIF(uFlags);

#ifndef _neptune
    NeedReschedule();
#endif
}
